package com.lan.evalulate;

import com.lan.antlr.ScriptParser;
import com.lan.model.Tv;
import com.lan.model.Type;
import com.lan.model.Variable;
import com.lan.model.symbol.Json;
import com.lan.ot.ClazzOt;
import com.lan.ot.Ot;
import com.lan.ot.Value;
import com.lan.ot.Vv;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

public class StackContext {


    private boolean traceStackFrame = false;

    /// 栈桢的管理
    private Stack<StackFrame> stack = new Stack<>();

    private void dumpStackFrame() {
        System.out.println("\nStack Frames ----------------");
        for (StackFrame frame : stack) {
            System.out.println(frame);
        }
        System.out.println("-----------------------------\n");
    }


    public void popStack() {
        stack.pop();
    }


    /**
     * 栈桢入栈。
     * 其中最重要的任务，是要保证栈桢的parent设置正确。否则，
     * (1)随着栈的变深，查找变量的性能会降低；
     * (2)甚至有可能找错栈桢，比如在递归(直接或间接)的场景下。
     *
     * @param frame
     */
    public void pushStack(StackFrame frame) {
        if (stack.size() > 0) {
            //从栈顶到栈底依次查找
            for (int i = stack.size() - 1; i > 0; i--) {
                StackFrame f = stack.get(i);
                if (f.getScope() == frame.getScope().getScope()) {
                    frame.setParent(f);
                    break;
                } else if (f.getScope().getScope() == frame.getScope().getScope()) {
                    frame.setParent(f.getParent());
                    break;
                }
            }
            if (frame.getParent() == null) {
                frame.setParent(stack.peek());
            }
        }
        stack.push(frame);
        if (traceStackFrame) {
            dumpStackFrame();
        }
    }


    /**
     * 解析属性表达式
     *
     * @param context
     * @param isGet   true 获取  false 设置
     * @return
     */
    public Value get(ScriptParser.PropertysExpContext context, boolean isGet) {
        Value rtn = null;
        String name = context.name().getText();
        for (int i = stack.size() - 1; i >= 0; i--) {
            StackFrame f = stack.get(i);
            Ot ot = f.getObject();
            Variable variable = ot.getVariable(name);
            if (variable != null) {
                Object val = ot.getValue(variable);
                if (val != null && val instanceof Value) {
                    rtn = (Value) val;
                } else {
                    rtn = new Vv(variable, val);
                    if (val == null) {
                        f.getObject().setValue(variable, rtn);
                    }
                }
                break;
            }
        }
        List<ScriptParser.PropertyExpContext> list = context.propertyExp();
        if (isGet) {
            if (rtn == null) {
                throw new RuntimeException("没有定义该变量:" + name);
            }
            if (CollectionUtils.isEmpty(list)) {
                return rtn;
            }
            if (rtn.getValue() == null) {
                throw new RuntimeException("该变量为空:" + name);
            }
            return get(name, rtn, list, isGet);
        } else {
            if (rtn == null) {
                if (CollectionUtils.isEmpty(list)) {
                    Variable variable = new Variable(name, null);
                    Value vv = new Vv(variable, null);
                    StackFrame f = stack.get(stack.size() - 1);
                    f.getObject().setValue(variable, vv);
                    return vv;
                }
                throw new RuntimeException("没有定义该变量:" + name);
            } else {
                if (CollectionUtils.isEmpty(list)) {
                    return rtn;
                }
                return get(name, rtn, list, isGet);
            }
        }
    }

    /**
     * <p>
     * 获取的常量值  Value (null,1)
     * 设置值  Value  key:key  value:Parent
     * </p>
     *
     * @param name
     * @param value
     * @param contextList
     * @param isGet
     * @return
     */
    public Value get(String name, Value value, List<ScriptParser.PropertyExpContext> contextList, boolean isGet) {
        //获取变量
        List<String> tList = new ArrayList<>();
        tList.add(name);

        Object object = value.getValue(); // 属性 . 属性 就是 json
        if (!(object instanceof ClazzOt)) {
            throw new RuntimeException("数据类型错误:" + name);
        }

        Variable variable;
        ClazzOt ot = (ClazzOt) object;
        Object data = ot.getData();     //
        for (int i = 0; i < contextList.size(); i++) {
            ScriptParser.PropertyExpContext item = contextList.get(i);
            if (item.INTEGER_LITERAL() != null) {
                Integer index = Integer.valueOf(item.INTEGER_LITERAL().getText());
                if (isGet) {
                    if (!(data instanceof List)) {
                        throw new RuntimeException(StringUtils.join(tList, ".") + "[" + index + "] 数据类型错误 不是集合");
                    }
                    List list = (List) data;
                    if (index >= list.size()) {
                        throw new RuntimeException("超出索引范围");
                    }
                    data = list.get(index);
                    if (data == null) {
                        throw new RuntimeException(StringUtils.join(tList, ".") + "[" + index + "] 该数据不能为空");
                    }
                } else {
                    // 数组里面允许添加 json  方法  表达式  只有常量值
                    // 可以修改   添加 一旦超过数组可以添加
                    // 问题  如何给数组修改  引用怎么带回去

                    variable = ot.getVariable(index + "");
                    if (contextList.size() - 1 == i) {
                        //最后一个  数组中只允许单纯数据  不允许用 function
                        variable = new Variable(Type.Expression_Array, index + "", null);
                        Value vv = new Vv(variable, ot);
                        return vv;
                    } else {

                        // 设置 下面的不对
//                        if (!(data instanceof List)) {
//                            throw new RuntimeException(StringUtils.join(tList, ".") + "[" + index + "] 数据类型错误 不是集合");
//                        }
//                        List list = (List) data;
//                        if (index >= list.size()) {
//                            throw new RuntimeException("超出索引范围");
//                        }
//                        data = list.get(index);
//                        if (data == null) {
//                            throw new RuntimeException(StringUtils.join(tList, ".") + "[" + index + "] 该数据不能为空");
//                        }
                        if (variable == null) {
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 该数据不能为空");
                        }
                        Type type = variable.getType();
                        Object v = variable.getValue();
                        /**
                         *  设置值
                         *
                         *  json 不变
                         *  ClazzOt 可变
                         *  json.json  to  ot.ot
                         */
                        // key 可能是方法 或者 Json  或者  LITERAL
                        // function 下面没有东西
                        // LITERAL 常量下没有东西
                        // 返回父节点
                        // 覆盖
                        if (type == Type.ClazzOt) {
                            ot = (ClazzOt) v;
                            data = ot.getData();
                        } else if (type == Type.JSON) {
                            //  json 原型 格式    将json to Ot
                            ClazzOt oot = new ClazzOt((Json) v);
                            ot.setValue(new Variable(Type.ClazzOt, index + ""), oot);
                            data = oot.getData();
                            ot = oot;
                        } else if (type == Type.Function) {
                            // 类型错误  当作最后一个可以有用
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 类型错误");
                        } else {
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 类型错误");
                        }

                    }
                }
            } else if (item.name() != null) {
                String key = item.name().getText();
                tList.add(key);
                if (isGet) {
                    if (!(data instanceof Map)) {
                        throw new RuntimeException(StringUtils.join(tList, ".") + " 数据类型错误");
                    }
                    Map<String, Object> map = (Map<String, Object>) data;
                    data = map.get(name);
                    if (data == null) {
                        throw new RuntimeException(StringUtils.join(tList, ".") + " 该数据不能为空");
                    }
                } else {
                    variable = ot.getVariable(key);
                    if (contextList.size() - 1 == i) {
                        // 覆盖 当前 key   value 是父节点
                        //设置新的值  scope
                        if (variable == null) {
                            variable = new Variable(Type.Expression_KEY, key, null);
                        } else {
                            variable.setType(Type.Expression_KEY);
                        }
                        // 当前 Ot 是父节点
                        Value vv = new Vv(variable, ot);
                        return vv;
                    } else {
                        if (variable == null) {
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 该数据不能为空");
                        }

                        Type type = variable.getType();
                        Object v = variable.getValue();
                        /**
                         *  设置值
                         *
                         *  json 不变
                         *  ClazzOt 可变
                         *  json.json  to  ot.ot
                         */
                        // key 可能是方法 或者 Json  或者  LITERAL
                        // function 下面没有东西
                        // LITERAL 常量下没有东西
                        // 返回父节点
                        // 覆盖
                        if (type == Type.ClazzOt) {
                            ot = (ClazzOt) v;
                            data = ot.getData();
                        } else if (type == Type.JSON) {
                            //  json 原型 格式    将json to Ot
                            ClazzOt oot = new ClazzOt((Json) v);
                            ot.setValue(new Variable(Type.ClazzOt, key), oot);
//                            oot.setParentOt(ot);
                            data = oot.getData();
                            ot = oot;
                        } else if (type == Type.Function) {
                            // 类型错误  当作最后一个可以有用
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 类型错误");
                        } else {
                            throw new RuntimeException(StringUtils.join(tList, ".") + " 类型错误");
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * k ->{a:b,c:d}
     * k -> []
     * 获取可以
     * 变量赋值不可以  变量赋值必须获取父节点
     * 找出通用方法 解决这个问题
     *
     * @param value
     * @param nameList
     * @return
     */
    private Value getValue(Value value, List<Tv> nameList) {
        if (value != null) {
            //获取变量
            Object object = value.getValue();
            for (int i = 1; i < nameList.size(); i++) {
                Tv tv = nameList.get(i);
                if (object == null) {
                    throw new RuntimeException(Tv.toString(nameList, i) + " is not undefined");
                }
                if (tv.getType() == Type.Integer) {
                    if (!(object instanceof List)) {
                        throw new RuntimeException(Tv.toString(nameList, i) + "  " + tv.getValue() + " 数据类型错误");
                    }
                    Integer index = (Integer) tv.getValue();
                    List list = (List) object;
                    if (index >= list.size()) {
                        throw new RuntimeException("超出索引范围");
                    }
                    object = list.get(index);
                } else if (tv.getType() == Type.String) {
                    String name = (String) tv.getValue();
                    if (!(object instanceof Map)) {
                        throw new RuntimeException(Tv.toString(nameList, i) + "  " + tv.getValue() + " 数据类型错误");
                    }
                    Map<String, Object> map = (Map<String, Object>) object;
                    object = map.get(name);
                }
            }
            Value vv = new Vv(null, object);
            return vv;
        } else {
            //新建变量
            if (nameList.size() > 1) {
                throw new RuntimeException(Tv.toString(nameList, ".") + " is not undefined");
            }
            Variable variable = new Variable(Tv.toString(nameList, "."), null);
            Value vv = new Vv(variable, null);
            StackFrame f = stack.get(stack.size() - 1);
            f.getObject().setValue(variable, vv);
            return vv;
        }
    }


    /**
     *
     * ERROR
     *
     * 26_10   范围   子节点  名称 等于父节点 名称   子节点是新建节点   覆盖了父节点  Error
     *
     * 不能等于范围节点名称   可以等于范围 内部的节点名称
     *
     *
     *
     *
     * @param name
     * @return
     */
    public Value get(String name) {
        Variable variable = null;
        Value rtn = null;
        for (int i = stack.size() - 1; i >= 0; i--) {
            StackFrame f = stack.get(i);
            Ot ot = f.getObject();
            variable = ot.getVariable(name);
            if (variable != null) {
                Object val = ot.getValue(variable);
                if (val != null && val instanceof Value) {
                    rtn = (Value) val;
                } else {
                    rtn = new Vv(variable, val);
                }
                break;
            }
        }
        return rtn;
    }


    public Value getValue(String name) {
        Variable variable = null;
        Value rtn = null;
        for (int i = stack.size() - 1; i >= 0; i--) {
            StackFrame f = stack.get(i);
            Ot ot = f.getObject();
            variable = ot.getVariable(name);
            if (variable != null) {
                Object val = ot.getValue(variable);
                if (val != null && val instanceof Value) {
                    rtn = (Value) val;
                } else {
                    rtn = new Vv(variable, val);
                    if (val == null) {
                        f.getObject().setValue(variable, rtn);
                    }
                }
            }
        }
        if (rtn == null) {
            StackFrame f = stack.get(stack.size() - 1);
            if (variable == null) {
                variable = new Variable(name, null);
            }
            rtn = new Vv(variable, null);
            f.getObject().setValue(variable, rtn);
        }
        return rtn;

    }


    /**
     * 怎么解决赋值问题
     * 新建变量可以
     * 简单变量可以
     * json 变量不可以
     *
     * @param nameList
     * @return
     */
    public Value getValue(List<Tv> nameList) {
        Variable variable = null;
        Value rtn = null;
        Tv tv = nameList.get(0);
        String name = (String) tv.getValue();
        for (int i = stack.size() - 1; i >= 0; i--) {
            StackFrame f = stack.get(i);
            Ot ot = f.getObject();
            variable = ot.getVariable(name);
            if (variable != null) {
                Object val = ot.getValue(variable);
                if (val != null && val instanceof Value) {
                    rtn = (Value) val;
                } else {
                    rtn = new Vv(variable, val);
                    if (val == null) {
                        f.getObject().setValue(variable, rtn);
                    }
                }
                break;
            }
        }
        if (rtn != null && nameList.size() > 1) {
            if (rtn.getValue() == null) {
                throw new RuntimeException(name + " is not undefined");
            }
            return getValue(rtn, nameList);
        } else if (rtn != null) {
            return rtn;
        } else {
            return getValue(null, nameList);
        }
    }

}
